package org.gnyxy.service.impl;

import org.gnyxy.constant.*;
import org.gnyxy.context.BaseContext;
import org.gnyxy.dto.CartDTO;
import org.gnyxy.dto.CartInfoDTO;
import org.gnyxy.dto.CartNumberDTO;
import org.gnyxy.entity.*;
import org.gnyxy.exception.BaseException;
import org.gnyxy.exception.ShoppingCartBusinessException;
import org.gnyxy.mapper.*;
import org.gnyxy.service.CartService;
import org.gnyxy.utils.BigDecimalUtil;
import org.gnyxy.vo.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class CartServiceImpl implements CartService {
    @Autowired
    private CartMapper cartMapper;
    @Autowired
    private CartInfoMapper cartInfoMapper;
    @Autowired
    private DishInfoMapper dishInfoMapper;
    @Autowired
    private DishMapper dishMapper;
    @Autowired
    private MaterialMapper materialMapper;



    /**
     * 添加购物车
     * @param cartDTO
     */
    @Override
    @Transactional //开启事务
    public void add(CartDTO cartDTO) {
        // 验证购物车数据传输对象
        validateCartDTO(cartDTO);

        Long dishId = cartDTO.getDishId();
        Long materialId = cartDTO.getMaterialId();
        Long userId = BaseContext.getCurrentId();

        // 如果购物车数据传输对象有ID，则处理带有ID的购物车
        if (cartDTO.getId() != null){
            handleCartWithId(cartDTO, dishId, userId);
        }
        // 如果购物车数据传输对象有菜品ID，则处理带有菜品ID的购物车
        else if (dishId != null){
            handleCartWithDishId(cartDTO, dishId, userId);
        }
        // 如果购物车数据传输对象有材料ID，则处理带有材料ID的购物车
        else {
            handleCartWithMaterialId(cartDTO, materialId, userId);
        }
    }

    /**
     * 验证购物车数据传输对象
     * @param cartDTO
     */
    private void validateCartDTO(CartDTO cartDTO) {
        if (cartDTO == null){
            throw new BaseException(MessageConstant.OBJECT_IS_NULL);
        }
        Long dishId = cartDTO.getDishId();
        Long materialId = cartDTO.getMaterialId();
        if (dishId == null && materialId == null) throw new ShoppingCartBusinessException(MessageConstant.ID_IS_NULL);
        if (dishId != null && materialId != null) throw new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_ERROR);
    }

    /**
     * 处理带有ID的购物车
     * @param cartDTO
     * @param dishId
     * @param userId
     */
    private void handleCartWithId(CartDTO cartDTO, Long dishId, Long userId) {
        BigDecimal number = BigDecimal.ONE;
        // 创建并插入购物车
        Cart cart = createAndInsertCart(userId, dishId, null, number);
        // 根据菜品ID查询菜品详情
        List<DishInfo> dishInfos = dishInfoMapper.getByDishId(dishId);
        if (dishInfos.isEmpty()) throw new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_ERROR);
        // 插入购物车详情
        insertCartInfos(cart, dishInfos);
    }

    /**
     * 处理带有菜品ID的购物车
     * @param cartDTO
     * @param dishId
     * @param userId
     */
    private void handleCartWithDishId(CartDTO cartDTO, Long dishId, Long userId) {
        // 获取该菜品的数量
        BigDecimal number = cartMapper.getNumberByDishIdAndUserId(dishId,userId);
        // 根据菜品ID查询菜品详情
        List<DishInfo> dishInfos = dishInfoMapper.getByDishId(dishId);
        if (dishInfos.isEmpty()) throw new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_ERROR);
        // 如果没有查到数量，则设置数量为1，并创建并插入购物车，然后插入购物车详情
        if (number == null){
            number = BigDecimal.ONE;
            Cart cart = createAndInsertCart(userId, dishId, null, number);
            insertCartInfos(cart, dishInfos);
        } else {
            // 如果查到数量，则更新购物车
            updateCartWithDishId(dishId, userId, number, dishInfos);
        }
    }

    /**
     * 处理带有材料ID的购物车
     * @param cartDTO
     * @param materialId
     * @param userId
     */
    private void handleCartWithMaterialId(CartDTO cartDTO, Long materialId, Long userId) {
        // 获取该材料的数量
        BigDecimal number = cartMapper.getNumberByMaterialIdAndUserId(materialId,userId);
        // 如果没有查到数量，则设置数量为1，并创建并插入购物车
        if (number == null){
            number = BigDecimal.ONE;
            createAndInsertCart(userId, null, materialId, number);
        }else {
            // 如果查到数量，则更新购物车
            Long cartId = cartMapper.getIdByMaterialIdAndUserId(materialId,userId);
            cartMapper.update(new CartNumberDTO(cartId,number.add(BigDecimal.ONE)));
        }
    }

    /**
     * 创建并插入购物车
     * @param userId
     * @param dishId
     * @param materialId
     * @param number
     * @return Cart
     */
    private Cart createAndInsertCart(Long userId, Long dishId, Long materialId, BigDecimal number) {
        Cart cart = new Cart().setUserId(userId)
                .setDishId(dishId)
                .setMaterialId(materialId)
                .setNumber(number)
                .setDeleted(DeleteConstant.IS_NOT_DELETED);
        cartMapper.insert(cart);
        return cart;
    }

    /**
     * 插入购物车详情
     * @param cart
     * @param dishInfos
     */
    private void insertCartInfos(Cart cart, List<DishInfo> dishInfos) {
        List<CartInfo> cartInfos = new ArrayList<>();
        for (DishInfo dishInfo : dishInfos){
            CartInfo cartInfo = new CartInfo()
                    .setCartId(cart.getId())
                    .setDeleted(DeleteConstant.IS_NOT_DELETED);
            BeanUtils.copyProperties(dishInfo,cartInfo);
            cartInfos.add(cartInfo);
        }
        cartInfoMapper.insert(cartInfos);
    }

    /**
     * 更新带有菜品ID的购物车
     * @param dishId
     * @param userId
     * @param number
     * @param dishInfos
     */
    private void updateCartWithDishId(Long dishId, Long userId, BigDecimal number, List<DishInfo> dishInfos) {
        List<Long> cartIds = cartMapper.getIdsByUserIdAndDish(userId);
        List<CartInfo> cartInfos = cartInfoMapper.getByCartIds(cartIds);
        Map<Long, BigDecimal> map = new HashMap<>();
        for (CartInfo cartInfo : cartInfos){
            map.put(cartInfo.getMaterialId(),cartInfo.getMaterialNumber());
        }
        for (DishInfo dishInfo : dishInfos){
            if (!map.containsKey(dishInfo.getMaterialId()) ||
                    !map.get(dishInfo.getMaterialId()).equals(dishInfo.getMaterialNumber())){
                CartDTO otherCartDTO = new CartDTO().setId(cartMapper.getMaxId() + 1)
                        .setDishId(dishInfo.getDishId());
                add(otherCartDTO);
                break;
            }
        }
        Long cartId = cartMapper.getIdByDishId(dishId);
        cartMapper.update(new CartNumberDTO(cartId,number.add(BigDecimal.ONE)));
    }


    /**
     * 查看当前用户的购物车以及详情
     * @param userId 用户ID
     * @return 购物车视图对象，包含总金额和默认总金额
     */
    @Override
    @Transactional
    public CartVOWithAmount getByUserId(Long userId) {
        // 校验用户ID，如果为空则抛出异常
        if (userId == null){
            throw new BaseException(MessageConstant.USER_NOT_LOGIN);
        }

        // 根据用户id获取该用户的购物车
        List<Cart> carts = cartMapper.getByUserId(userId);
        if (carts == null || carts.isEmpty()) return null;

        // 将购物车信息转换为视图对象
        List<CartVO> cartVOS = carts.stream().map(this::convertToCartVO).collect(Collectors.toList());

        // 获取购物车ID列表
        List<Long> cartIds = cartVOS.stream().map(CartVO::getId).collect(Collectors.toList());

        // 获取购物车详情信息
        List<CartInfo> cartInfos = cartInfoMapper.getByCartIds(cartIds);

        // 创建hashmap存储购物车详情信息
        Map<String, BigDecimal> cartInfoMap = createCartInfoMap(cartInfos);

        // 处理购物车视图对象列表
        handleCartVOs(cartVOS, cartInfoMap);

        // 计算购物车总金额和默认总金额
        BigDecimal bigAmount = calculateBigAmount(cartVOS);
        BigDecimal defaultBigAmount = calculateDefaultBigAmount(cartVOS);

        return new CartVOWithAmount().setBigAmount(bigAmount)
                .setDefaultBigAmount(defaultBigAmount)
                .setCartVOS(cartVOS);
    }

    /**
     * 将购物车对象转换为购物车视图对象
     * @param cart 购物车对象
     * @return 购物车视图对象
     */
    private CartVO convertToCartVO(Cart cart) {
        CartVO cartVO = new CartVO();
        BeanUtils.copyProperties(cart,cartVO);
        return cartVO;
    }

    /**
     * 创建购物车详情的Map
     * @param cartInfos 购物车详情列表
     * @return 购物车详情的Map
     */
    private Map<String, BigDecimal> createCartInfoMap(List<CartInfo> cartInfos) {
        Map<String, BigDecimal> map = new HashMap<>();
        for (CartInfo cartInfo : cartInfos) {
            // 存入map中
            map.put("CartId:" +cartInfo.getCartId() +
                            "DishId:" + cartInfo.getDishId() +
                            "MaterialId:" +cartInfo.getMaterialId(),
                    cartInfo.getMaterialNumber());
        }
        return map;
    }

    /**
     * 处理购物车视图对象列表
     * @param cartVOS 购物车视图对象列表
     * @param cartInfoMap 购物车详情的Map
     */
    private void handleCartVOs(List<CartVO> cartVOS, Map<String, BigDecimal> cartInfoMap) {
        Iterator<CartVO> iterator = cartVOS.iterator();
        while (iterator.hasNext()) {
            CartVO cartVO = iterator.next();
            Long dishId = cartVO.getDishId();
            Long materialId = cartVO.getMaterialId();
            // 如果购物车视图对象有菜品ID，则处理带有菜品ID的购物车
            if (dishId != null){
                handleCartWithDishId(iterator, cartVO, dishId, cartInfoMap);
            }
            // 如果购物车视图对象有材料ID，则处理带有材料ID的购物车
            else if (materialId != null) {
                handleCartWithMaterialId(iterator, cartVO, materialId);
            }
        }
    }

    /**
     * 处理带有菜品ID的购物车
     * @param iterator 购物车视图对象迭代器
     * @param cartVO 购物车视图对象
     * @param dishId 菜品ID
     * @param cartInfoMap 购物车详情的Map
     */
    private void handleCartWithDishId(Iterator<CartVO> iterator, CartVO cartVO, Long dishId, Map<String, BigDecimal> cartInfoMap) {
        Dish dish = dishMapper.getById(dishId);
        // 如果菜品不存在或者被删除或者被禁用，则从购物车和购物车详情中删除该菜品并从购物车视图对象列表中移除
        if (dish == null || dish.getDeleted().equals(DeleteConstant.IS_DELETED) ||
                dish.getStatus().equals(StatusConstant.DISABLE)){
            deleteCartByDishId(dishId, cartVO.getUserId());
            iterator.remove();
            return;
        }
        // 获取菜品详情
        List<DishInfo> dishInfos = dishInfoMapper.getByDishId(dishId);
        if (dishInfos == null || dishInfos.isEmpty()) return;
        // 更新购物车视图对象的菜品信息
        updateCartWithDishInfo(cartVO, dishInfos, dish, cartInfoMap);
    }

    /**
     * 处理带有材料ID的购物车
     * @param iterator 购物车视图对象迭代器
     * @param cartVO 购物车视图对象
     * @param materialId 材料ID
     */
    private void handleCartWithMaterialId(Iterator<CartVO> iterator, CartVO cartVO, Long materialId) {
        Material material = materialMapper.getMaterialById(materialId);
        // 如果材料不存在或者被删除或者被禁用，则从购物车中删除该材料并从购物车视图对象列表中移除
        if (material == null || material.getDeleted().equals(DeleteConstant.IS_DELETED) ||
                material.getStatus().equals(StatusConstant.DISABLE)){
            deleteCartByMaterialId(materialId, cartVO.getUserId());
            iterator.remove();
            return;
        }
        // 更新购物车视图对象的材料信息
        updateCartWithMaterialInfo(cartVO, material);
    }

    /**
     * 根据菜品ID删除购物车和购物车详情
     * @param dishId 菜品ID
     * @param userId 用户ID
     */
    private void deleteCartByDishId(Long dishId, Long userId) {
        cartMapper.deleteByDishId(dishId,userId);
        List<Long> deleteCartIds = cartMapper.getIdsByUserIdAndDish(userId);
        cartInfoMapper.deleteByCartIds(deleteCartIds);
    }

    /**
     * 根据材料ID删除购物车
     * @param materialId 材料ID
     * @param userId 用户ID
     */
    private void deleteCartByMaterialId(Long materialId, Long userId) {
        cartMapper.deleteByMaterialId(materialId,userId);
    }

    /**
     * 更新购物车视图对象的菜品信息
     * @param cartVO 购物车视图对象
     * @param dishInfos 菜品详情列表
     * @param dish 菜品对象
     * @param cartInfoMap 购物车详情的Map
     */
    private void updateCartWithDishInfo(CartVO cartVO, List<DishInfo> dishInfos, Dish dish, Map<String, BigDecimal> cartInfoMap) {
        List<Long> materialIds = new ArrayList<>();
        boolean isSame = true;
        for (DishInfo dishInfo : dishInfos){
            Long infoMaterialId = dishInfo.getMaterialId();
            materialIds.add(infoMaterialId);
            String key = "CartId:" +cartVO.getId() +
                    "DishId:" + dish.getId() +
                    "MaterialId:" +infoMaterialId;
            if (!cartInfoMap.containsKey(key) || !cartInfoMap.get(key)
                    .equals(dishInfo.getMaterialNumber())){
                isSame = false;
            }
        }
        List<Material> materials = materialMapper.getByIds(materialIds);
        if (materials == null || materials.isEmpty()){
            cartVO.setAmount(null).setDefaultAmount(null);
            return;
        }
        BigDecimal number = cartVO.getNumber();
        if (isSame){

            BigDecimal defaultPrice = dish.getDefaultPrice();
            BigDecimal price = dish.getPrice();
            cartVO.setPrice(price == null ? defaultPrice : price)
                    .setDefaultPrice(defaultPrice)
                    .setAmount(BigDecimalUtil.round(price.multiply(number)))
                    .setDefaultAmount(BigDecimalUtil.round(defaultPrice.multiply(number)));
        } else {

            CartInfoVOWithAmount cartInfos = getCartInfos(cartVO.getId());
            BigDecimal bigAmount = cartInfos.getBigAmount();
            BigDecimal defaultBigAmount = cartInfos.getDefaultBigAmount();
            cartVO.setPrice(bigAmount)
                    .setDefaultPrice(defaultBigAmount)
                    .setAmount(bigAmount.multiply(number))
                    .setDefaultAmount(defaultBigAmount.multiply(number));
        }


        cartVO.setImage(dish.getImage())
                .setDishName(dish.getName());
    }

    /**
     * 更新购物车视图对象的材料信息
     * @param cartVO 购物车视图对象
     * @param material 材料对象
     */
    private void updateCartWithMaterialInfo(CartVO cartVO, Material material) {
        BigDecimal price = BigDecimalUtil.round(material.getPrice());
        BigDecimal defaultPrice = BigDecimalUtil.round(material.getDefaultPrice());
        BigDecimal number = cartVO.getNumber();
        cartVO.setPrice(price)
                .setDefaultPrice(defaultPrice)
                .setAmount(price == null ?
                        defaultPrice.multiply(number)
                        : price.multiply(number))
                .setDefaultAmount(defaultPrice.multiply(number))
                .setMaterialName(material.getName())
                .setImage(material.getImage());
    }

    /**
     * 计算购物车总金额
     * @param cartVOS 购物车视图对象列表
     * @return 购物车总金额
     */
    private BigDecimal calculateBigAmount(List<CartVO> cartVOS) {
        return BigDecimalUtil.round(cartVOS.stream()
                .filter(Objects::nonNull)
                .map(cartVO -> cartVO.getAmount().multiply(cartVO.getNumber()))
                .reduce(BigDecimal.ZERO, BigDecimal::add));
    }

    /**
     * 计算购物车默认总金额
     * @param cartVOS 购物车视图对象列表
     * @return 购物车默认总金额
     */
    private BigDecimal calculateDefaultBigAmount(List<CartVO> cartVOS) {
        return BigDecimalUtil.round(cartVOS.stream()
                .filter(Objects::nonNull)
                .map(cartVO -> cartVO.getDefaultAmount().multiply(cartVO.getNumber()))
                .reduce(BigDecimal.ZERO, BigDecimal::add));
    }


    /**
     * 获取购物车详情信息
     * @param cartId 购物车ID
     * @return 购物车信息和相关金额
     */
    @Override
    public CartInfoVOWithAmount getCartInfos(Long cartId) {
        // 查询该购物车下的详情
        List<CartInfo> cartInfos = Optional.ofNullable(cartInfoMapper.getInfosByCartId(cartId))
                .filter(infos -> !infos.isEmpty())
                .orElseThrow(() -> new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_ERROR));

        // 获取食材主键集合并查询食材
        List<Material> materials = Optional.ofNullable(materialMapper.getByIds(cartInfos.stream()
                        .map(CartInfo::getMaterialId)
                        .collect(Collectors.toList())))
                .filter(mats -> !mats.isEmpty())
                .orElseThrow(() -> new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_ERROR));

        // 创建VO并拷贝属性
        List<CartInfoVO> cartInfoVOS = cartInfos.stream().map(this::convertToVO).collect(Collectors.toList());

        // 计算总价和默认总价
        BigDecimal bigAmount = BigDecimal.ZERO;
        BigDecimal defaultBigAmount = BigDecimal.ZERO;

        // 完成Bean拷贝
        for (Material material : materials) {
            for (CartInfoVO cartInfoVO : cartInfoVOS) {
                if (cartInfoVO.getMaterialId().equals(material.getId())){
                    BigDecimal defaultPrice = material.getDefaultPrice();
                    BigDecimal price = material.getPrice();
                    BigDecimal defaultAmount = defaultPrice.multiply(cartInfoVO.getMaterialNumber());
                    BigDecimal amount = price.multiply(cartInfoVO.getMaterialNumber());
                    defaultBigAmount = defaultBigAmount.add(defaultAmount);
                    bigAmount = bigAmount.add(amount);
                    cartInfoVO.setImage(material.getImage())
                            .setUnit(material.getUnit())
                            .setRange(material.getRange())
                            .setMaterialName(material.getName())
                            .setDefaultPrice(defaultPrice)
                            .setPrice(price)
                            .setDefaultAmount(defaultAmount)
                            .setAmount(amount);
                }
            }
        }

        // 根据dish的主键查询折扣
        BigDecimal discounts = Optional.ofNullable(dishMapper.getById(cartInfos.get(0).getDishId()))
                .map(Dish::getDiscount)
                .filter(discount -> discount.doubleValue() > 0 && discount.doubleValue() <= 1)
                .map(BigDecimalUtil::round)
                .orElseThrow(() -> new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_ERROR));

        return new CartInfoVOWithAmount()
                .setCartInfoVOS(cartInfoVOS)
                .setDefaultBigAmount(BigDecimalUtil.round(defaultBigAmount))
                .setBigAmount(BigDecimalUtil.round(bigAmount.multiply(discounts)))
                .setDiscount(discounts);
    }

    private CartInfoVO convertToVO(CartInfo cartInfo) {
        CartInfoVO cartInfoVO = new CartInfoVO();
        BeanUtils.copyProperties(cartInfo, cartInfoVO);
        return cartInfoVO;
    }



    @Override
    @Transactional
    public void deleteCartsByIds(List<Long> cartIds) {
        cartMapper.deleteByIds(cartIds);
        cartInfoMapper.deleteByCartIds(cartIds);
    }

    /**
     * 更新菜品中的材料数量
     * @param cartInfoDTO
     */
    @Override
    public void updateMaterialNumber(CartInfoDTO cartInfoDTO) {
        // 校验
        if (cartInfoDTO == null){
            throw new BaseException(MessageConstant.OBJECT_IS_NULL);
        }
        // 更新
        cartInfoMapper.update(cartInfoDTO);
    }

    /**
     * 更新菜品数量
     * @param cartNumberDTO
     */
    @Override
    public void updateCartNumber(CartNumberDTO cartNumberDTO) {
        // 校验
        if (cartNumberDTO == null){
            throw new BaseException(MessageConstant.OBJECT_IS_NULL);
        }
        // 更新
        cartMapper.update(cartNumberDTO);
    }

    /**
     * 删除购物车以及详情
     * @param id
     */
    @Override
    @Transactional
    public void deleteCart(Long id) {
        if (id == null){
            throw new BaseException(MessageConstant.ID_IS_NULL);
        }
        Cart cart = cartMapper.getById(id);
        cart.setDeleted(DeleteConstant.IS_DELETED);
        cartMapper.delete(cart);
        if (cart.getDishId() != null){
            cartInfoMapper.deleteByCartId(id);
        }
    }

    /**
     * 获取当前购物车id
     * @param userId
     * @return
     */
    @Override
    public Long getIdByUserId(Long userId) {
        if (userId == null){
            throw new BaseException(MessageConstant.USER_NOT_LOGIN);
        }

        return cartMapper.getIdByUserId(userId);
    }
}
